//
//  ThumbSelectorView.swift
//  Pods
//
//  Created by Henry on 06/04/2017.
//
//

import UIKit
import AVFoundation


/// A delegate to be notified of when the thumb position has changed. Useful to link an instance of the ThumbSelectorView to a
/// video preview like an `AVPlayer`.
public protocol ThumbSelectorViewDelegate: class {
    func didChangeThumbPosition(_ imageTime: CMTime)
}

/// A view to select a specific time of an `AVAsset`. It is composed of an asset preview within a scroll view, and a thumb view
/// to select a precise time of the video. Set the `asset` property to load the video, and use the `selectedTime` property to
// retrieve the exact frame of the asset that was selected.
public class ThumbSelectorView: AVAssetTimeSelector {

    public var thumbBorderColor: UIColor = .white {
        didSet {
            thumbView.layer.borderColor = thumbBorderColor.cgColor
        }
    }

    private let thumbView = UIImageView()
    private let dimmingView = UIView()

    private var currentThumbConstraint: CGFloat = 0

    private var generator: AVAssetImageGenerator?

    public weak var delegate: ThumbSelectorViewDelegate?
    
    let maskLayer = CAShapeLayer()
    

    // MARK: - View & constraints configurations

    override func setupSubviews() {
        super.setupSubviews()
        setupDimmingView()
        setupThumbView()
    }
    
    override func reloadViews() {
        super.reloadViews()
        constrainViews()
        delay(0.5) {
            self.configureMasks(0)
        }
    }

    private func setupDimmingView() {

        dimmingView.translatesAutoresizingMaskIntoConstraints = false
        dimmingView.isUserInteractionEnabled = true
        dimmingView.backgroundColor = UIColor.black.withAlphaComponent(0.3)
        addSubview(dimmingView)
        
        let tap = UITapGestureRecognizer(target: self, action: #selector(ThumbSelectorView.handleTapGesture(_:)))
        dimmingView.addGestureRecognizer(tap)
    }

    private func setupThumbView() {

        thumbView.translatesAutoresizingMaskIntoConstraints = false
        thumbView.layer.borderWidth = 3.0
        thumbView.layer.borderColor = thumbBorderColor.cgColor
        thumbView.isUserInteractionEnabled = true
        thumbView.contentMode = .scaleAspectFill
        thumbView.clipsToBounds = true
        thumbView.backgroundColor = UIColor.clear
        addSubview(thumbView)
        
        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ThumbSelectorView.handlePanGesture(_:)))
        thumbView.addGestureRecognizer(panGestureRecognizer)
    }
    
    func constrainViews() {
        
        dimmingView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
        
        thumbView.snp.makeConstraints { (make) in
            make.left.equalToSuperview()
            make.height.equalToSuperview()
            make.centerY.equalToSuperview()
            if let imageWidth = thumbnailImageWidth {
                make.width.equalTo(imageWidth)
            } else {
                make.width.equalTo(thumbView.snp.height)
            }
        }
    }
    
    private func configureMasks(_ position: CGFloat) {
//        let fromPath = maskLayer.path
        
        var frame = thumbView.frame
        frame.origin.x = position
        let highlightedPath = UIBezierPath(rect: frame)
        let toPath = UIBezierPath(rect: dimmingView.bounds)
        toPath.append(highlightedPath)
        
        maskLayer.path = toPath.cgPath
        maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
        dimmingView.layer.mask = maskLayer
        
        // animatedMask
//        let animation = CABasicAnimation(keyPath: "path")
//        animation.duration = 0.1
//        animation.fromValue = fromPath
//        animation.toValue = toPath
//        maskLayer.add(animation, forKey: nil)
    }

    // MARK: - Gesture handling

    @objc func handlePanGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
        guard let superView = gestureRecognizer.view?.superview else { return }

        switch gestureRecognizer.state {

        case .began:
            self.currentThumbConstraint = thumbView.frame.origin.x
            updateSelectedTime()
        case .changed:

            var translation = gestureRecognizer.translation(in: superView)
            translation.x += currentThumbConstraint
            updateThumbConstraint(with: translation)
            layoutIfNeeded()
            updateSelectedTime()

        case .cancelled, .ended, .failed, .possible:
            updateSelectedTime()
        default: break
        }
    }
    
    @objc func handleTapGesture(_ gestureRecognizer: UIGestureRecognizer) {
        var location = gestureRecognizer.location(in: gestureRecognizer.view)
        var imageWidth = thumbView.frame.size.height
        if let width = thumbnailImageWidth {
            imageWidth = width
        }
        location.x -= imageWidth / 2
        updateThumbConstraint(with: location)
        layoutIfNeeded()
        updateSelectedTime()
    }

    private func updateThumbConstraint(with translation: CGPoint) {
        let maxConstraint = assetPreview.contentSize.width - thumbView.frame.width
        let newConstraint = min(max(0, translation.x), maxConstraint)
        thumbView.snp.updateConstraints { (make) in
            make.left.equalTo(newConstraint)
        }
        configureMasks(newConstraint)
    }

    // MARK: - Thumbnail Generation

    override func assetDidChange(newAsset: AVAsset?) {
        if let asset = newAsset {
            setupThumbnailGenerator(with: asset)
            thumbView.snp.updateConstraints { (make) in
                make.left.equalTo(0)
            }
            updateSelectedTime()
        }
        super.assetDidChange(newAsset: newAsset)
    }

    private func setupThumbnailGenerator(with asset: AVAsset) {
        generator = AVAssetImageGenerator(asset: asset)
        generator?.appliesPreferredTrackTransform = true
        generator?.requestedTimeToleranceAfter = CMTime.zero
        generator?.requestedTimeToleranceBefore = CMTime.zero
        generator?.maximumSize = getThumbnailFrameSize(from: asset) ?? CGSize.zero
    }

    private func getThumbnailFrameSize(from asset: AVAsset) -> CGSize? {
        guard let track = asset.tracks(withMediaType: AVMediaType.video).first else { return nil}

        let assetSize = track.naturalSize.applying(track.preferredTransform)

        let maxDimension = max(assetSize.width, assetSize.height)
        let minDimension = min(assetSize.width, assetSize.height)
        let ratio = maxDimension / minDimension
        let side = thumbView.frame.height * ratio * UIScreen.main.scale
        return CGSize(width: side, height: side)
    }

    // MARK: - Time & Position Equivalence

    override var durationSize: CGFloat {
        return assetPreview.contentSize.width - thumbView.frame.width
    }

    /// The currently selected time of the asset.
    public var selectedTime: CMTime? {
        let thumbPosition = thumbView.center.x + assetPreview.contentOffset.x - (thumbView.frame.width / 2)
        return getTime(from: thumbPosition)
    }

    private func updateSelectedTime() {
        if let selectedTime = selectedTime {
            delegate?.didChangeThumbPosition(selectedTime)
        }
    }

    // MARK: - UIScrollViewDelegate

    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        updateSelectedTime()
    }
}
